<?php

/**
  *
  * ENhanced Categorise PLUG-IN FOR NucleusCMS
  * PHP versions 4 and 5
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
  * of the License, or (at your option) any later version.
  * (see nucleus/documentation/index.html#license for more info)
  *
  * @author    shizuki
  * @copyright 2007 shizuki
  * @license   http://www.gnu.org/licenses/gpl.txt  GNU GENERAL PUBLIC LICENSE Version 2, June 1991
  * @version   $Id: NP_CategoryEX.php 57 2009-03-03 17:53:55Z shizuki17xx $
  * @link      http://shizuki.kinezumi.net/
  *
  * 0.0.1      Initial version.
  *
  **/

require_once(dirname(__FILE__) . '/sharedlibs/sharedlibs.php');
require_once('shizukiShared/shizukiSharedFunctions.php');
require_once('shizukiShared/databaseMethod.php');

class NP_CategoryEX extends shizukiSharedFunctions
{

	// {{{ function getVersion()

	/**
	  * version of the plugin
	  *
	  * @access public
	  **/
	function getVersion()
	{
		return '0.0.7-2007/10/15-19:13:17';
	}

	// }}}
	// {{{ function getEventList()

	/**
	  * get Nucleus event list for plugin action
	  *
	  * @access private
	  **/
	function getEventList()
	{
		return array(
			'PostAddBlog',
			'PostDeleteBlog',
			'PostAddCategory',
			'PostMoveCategory',
			'PostDeleteCategory',
			'PostAddItem',
			'PostUpdateItem',
			'PostDeleteItem',
			'PrePluginOptionsUpdate',
			'PreSendContentType',
//			'initSkinParse',
			);
	}

	// }}}
	// {{{ function getTableList()

	/**
	  * get database tables for plugin used
	  *
	  * @access private
	  **/
	function getTableList()
	{
		return array(
			_NP_CATEGORYEX_TREE_TABLE,
			_NP_CATEGORYEX_ITEM_TABLE,
			_NP_CATEGORYEX_ECAT_TABLE,
			_NP_CATEGORYEX_ALIAS_TABLE,
			);
	}

	// }}}
	// {{{ function init() (constructor)

	/**
	  * Construct this plugin
	  *
	  * @access private
	  **/
	function init()
	{
		global $CONF;
		if ($CONF['URLMode'] == 'pathinfo' && empty($CONF['ECategoryKey'])) {
			$CONF['ECategoryKey'] = $this->getRequestName();
		}
		if (!$this->dbh) {
			global $MYSQL_HOST, $MYSQL_USER, $MYSQL_PASSWORD, $MYSQL_DATABASE;
			$this->dbh = new databaseMethod($MYSQL_HOST, $MYSQL_DATABASE, $MYSQL_USER, $MYSQL_PASSWORD, $this);
		}
		if (!defined('_NP_CATEGORYEX_DESC') ) {
			$this->includeLanguageFile();
		}
		if (!defined('_NP_CATEGORYEX_TABLE_DEF') ) {
			define('_NP_CATEGORYEX_TABLE_DEF',   true);
			define('_NP_CATEGORYEX_TREE_TABLE',  sql_table('plug_category_ex_tree'));
			define('_NP_CATEGORYEX_ECAT_TABLE',  sql_table('plug_category_ex_ecats'));
			define('_NP_CATEGORYEX_ITEM_TABLE',  sql_table('plug_category_ex_items'));
			define('_NP_CATEGORYEX_ALIAS_TABLE', sql_table('plug_category_ex_alias'));
		}
	}

	// }}}
	// {{{ function install()

	/**
	  * plugin install actions
	  *
	  * @access private
	  **/
	function install()
	{
		$this->createTreeTable();
		$this->createEcatTable();
		$this->createItemTable();
		$this->createAliasTable();
		$items = $this->convertItemCategoryTable();
	}

	// }}}
	// {{{ function unInstall()

	/**
	  * plugin unInstall actions
	  *
	  * @access private
	  **/
	function unInstall()
	{
//		if ($this->getOption('npecat_tabledel') == 'yes') {
			$this->dbh->dropTable(_NP_CATEGORYEX_TREE_TABLE);
			$this->dbh->dropTable(_NP_CATEGORYEX_ITEM_TABLE);
			$this->dbh->dropTable(_NP_CATEGORYEX_ECAT_TABLE);
			$this->dbh->dropTable(_NP_CATEGORYEX_ALIAS_TABLE);
//		}
	}

	// }}}

// ------------------------  install functions start  -------------------------

	// {{{ function createTreeTable()

	/**
	  * Create category table
	  *
	  * @access private
	  **/
	function createTreeTable()
	{
		$fields = array();
		$index  = array();
		$fields = array(
			'ecatid' => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true, 'autoincrement' => true,
				),
			'blogid' => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true,
				),
			'parent' => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true,
				),
			'alias'  => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true,
				),
			'tleft'  => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true,
				),
			'tright' => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true,
				),
			'corder' => array(
				'type' => 'integer', 'length' => 4,   'notnull' => true, 'default' => 100,
				),
			'cname'  => array(
				'type' => 'varchar', 'length' => 255, 'notnull' => true,
				),
			'cdesc'  => array(
				'type' => 'varchar', 'length' => 255,
				),
			);
		$index  = array(
			'prymary' => 'ecatid',
			'index'   => array(
				'tleft'  => 'tleft',
				'tright' => 'tright',
				'cname'  => 'cname',
				)
			);
		return $this->dbh->createNewTable(_NP_CATEGORYEX_TREE_TABLE, $fields, $index);
	}

	// }}}
	// {{{ function createEcatTable()

	/**
	  * Create category table by which an item is included
	  *
	  * @access private
	  **/
	function createEcatTable()
	{
		$fields = array();
		$index  = array();
		$fields = array(
			'id'     => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true, 'autoincrement' => true,
				),
			'itemid' => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true,
				),
			'ecats'  => array(
				'type' => 'text',    'length' => 16777215,
				),
			);
		$index  = array(
			'prymary' => 'id',
			'index'   => array(
				'itemid'  => 'itemid',
				)
			);
		return $this->dbh->createNewTable(_NP_CATEGORYEX_ECAT_TABLE, $fields, $index);
	}

	// }}}
	// {{{ function createItemTable()

	/**
	  * Create item table a category includes
	  *
	  * @access private
	  **/
	function createItemTable()
	{
		$fields = array();
		$index  = array();
		$fields = array(
			'id'      => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true, 'autoincrement' => true,
				),
			'idkey'   => array(
				'type' => 'varchar', 'length' => 4, 'notnull' => true,
				),
			'keyid'   => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true,
				),
			'itemids' => array(
				'type' => 'text',    'length' => 16777215,
				),
			);
		$index  = array(
			'prymary' => 'id',
			'index'   => array(
				'idkey'  => 'idkey',
				)
			);
		return $this->dbh->createNewTable(_NP_CATEGORYEX_ITEM_TABLE, $fields, $index);
	}

	// }}}
	// {{{ function createAliasTable()

	/**
	  * Create alias table
	  *
	  * @access private
	  **/
	function createAliasTable()
	{
		$fields = array();
		$index  = array();
		$fields = array(
			'id'      => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true, 'autoincrement' => true,
				),
			'alias'   => array(
				'type' => 'varchar', 'length' => 4, 'notnull' => true,
				),
			'aliasid' => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true,
				),
			'ecatid'  => array(
				'type' => 'integer', 'length' => 4, 'notnull' => true,
				),
			);
		$index  = array(
			'prymary' => 'id',
			'index'   => array(
				'ecatid'  => 'ecatid',
				'aliasid' => 'aliasid',
				'alias'   => 'alias',
				)
			);
		return $this->dbh->createNewTable(_NP_CATEGORYEX_ALIAS_TABLE, $fields, $index);
	}

	// }}}
	// {{{ function getStrItems(&$stmt, $id)

	/**
	  * Get itemids a category includes
	  *
	  * @access private
	  *
	  * @param object
	  *     PDOstatement->prepare object
	  * @param int
	  *     Category ID or Subcategory ID
	  *
	  * @return string
	  *     Include iteids
	  **/
	function getStrItems(&$stmt, $id)
	{
		$stmt->execute($id);
		$item = $stmt->fetchAll(PDO::FETCH_NUM);
		$items = array();
		foreach ($item as $itm) {
			$items[] = $itm[0];
		}
		return implode(',', $items);
	}

	/**
	  * Convert from Nucleus's default category (and NP_MultipleCategories's subcategory)
	  * to NP_CategoryEX's enhanced category.
	  *
	  * @access private
	  **/
	function convertItemCategoryTable()
	{
		$mplugin = $this->pluginCheck('MultipleCategories');
		$blogids = $this->dbh->getColumnAssoc(sql_table('blog'), array('column' => 'bnumber'));
		$preBItm = $this->dbh->dbh->prepare('SELECT inumber FROM ' . sql_table('item') . ' WHERE iblog = ?');
		$preCItm = $this->dbh->dbh->prepare('SELECT inumber FROM ' . sql_table('item') . ' WHERE icat = ?');
		if (is_object($mplugin)) {
			$itemTbl = sql_table('plug_multiple_categories');
			$preMItm = $this->dbh->dbh->prepare('SELECT `item_id` FROM ' . $itemTbl . ' WHERE categories REGEXP ?');
		}
		$prepare = array();
		foreach ($blogids['bnumber'] as $blogid) {
			$prepare[] = array(
				'blog',
				$blogid,
				$this->getStrItems(&$preBItm, array($blogid))
			);
			$parts = array(
				'column' => '*',
				'where'  => array(
					'cblog' => array('=', $blogid),
				)
			);
			$categories = $this->dbh->getAllRowAssoc(sql_table('category'), $parts);
			foreach ($categories as $category) {
				$ecatid    = $this->createECategory($category['cblog'], $category['cname'], $category['cdesc']);
				if (is_object($mplugin)) {
					$pWhere  = sprintf('(^|,)%d(,|$)', $category['catid']);
					$catItem = $this->getStrItems(&$preMItm, array($pWhere));
					if ($catItem) {
						$catItem .= ',';
					}
					$catItem .= $this->getStrItems(&$preCItm, array($category['catid']));
					$catItem  = explode(',', $catItem);
					$catItem  = array_unique($catItem);
					$catItem  = implode(',', $catItem);
					$subcats  = $this->buildSubcatTree($category['cblog'], $category['catid'], $ecatid);
					if (!empty($subcats)) {
						foreach ($subcats['subcats'] as $subcat) {
							$prepare[] = $subcat;
						}
						foreach ($subcats['alias'] as $subcat) {
							$alias[] = $subcat;
						}
					}
				} else {
					$catItem = $this->getStrItems(&$preCItm, array($category['catid']));
				}
				$prepare[] = array(
					'ecat',
					$ecatid,
					$catItem
				);
				$alias[] = array(
					'cat',
					$category['catid'],
					$ecatid
				);
			}
		}
		$insertQ = 'INSERT INTO '
				 . _NP_CATEGORYEX_ITEM_TABLE
				 . ' (`idkey`, `keyid`, `itemids`) VALUES (?, ?, ?)';
		$prepIns = $this->dbh->dbh->prepare($insertQ);
		foreach ($prepare as $catItem) {
			$prepIns->execute($catItem);
		}
		$insertQ = 'INSERT INTO '
				 . _NP_CATEGORYEX_ALIAS_TABLE
				 . ' (`alias`, `aliasid`, `ecatid`) VALUES (?, ?, ?)';
		$prepIns = $this->dbh->dbh->prepare($insertQ);
		foreach ($alias as $catAlias) {
			$prepIns->execute($catAlias);
		}
		$parts  = array('column' => 'inumber');
		$items  = $this->dbh->getAllRowAssoc(sql_table('item'), $parts);
		$query  = 'INSERT INTO '
				. _NP_CATEGORYEX_ECAT_TABLE
				. ' (`itemid`, `ecats`) VALUES (?, ?)';
		$insert = $this->dbh->dbh->prepare($query);
		$query  = 'SELECT `keyid` FROM '
				. _NP_CATEGORYEX_ITEM_TABLE
				. ' WHERE `itemids` REGEXP ?'
				. " AND   `idkey` = 'ecat'";
		$select = $this->dbh->dbh->prepare($query);
		foreach ($items as $item) {
			$where = sprintf('(^|,)%d(,|$)', $item['inumber']);
			$ecats = $this->getStrItems(&$select, array($where));
			$insert->execute(array($item['inumber'], $ecats));
		}
	}

	// }}}
	// {{{ function buildSubcatTree($blogid, $catid, $parentid, $subparent = 0)

	/**
	  * Build subcategory and them children's subcategory tree
	  *
	  * @access private
	  *
	  * @param int
	  *     Parent blog ID
	  * @param int
	  *     Parent category ID
	  * @param int
	  *     Parent enhanced category ID
	  * @param int
	  *     Parent subcategory ID
	  *
	  * @return array
	  *     Subcategory data array
	  *     Subcategory alias array
	  **/
	function buildSubcatTree($blogid, $catid, $parentid, $subparent = 0)
	{
		$subTable = sql_table('plug_multiple_categories_sub');
		$itemTbl  = sql_table('plug_multiple_categories');
		$subQuery = 'SELECT `item_id` FROM ' . $itemTbl . ' WHERE subcategories REGEXP ?';
		$prepSItm = $this->dbh->dbh->prepare($subQuery);
		$parts = array(
			'column' => '*',
			'where'  => array(
				'catid'    => array('=', $catid),
				'parentid' => array('=', $subparent),
			)
		);
		$subcategories = $this->dbh->getAllRowAssoc($subTable, $parts);
		if (!empty($subcategories)) {
			foreach ($subcategories as $subcategory) {
				$sname  = $subcategory['sname'];
				$sdesc  = $subcategory['sdesc'];
				$ordid  = $subcategory['ordid'];
				$ecatid = $this->createECategory($blogid, $sname, $sdesc, $parentid, $ordid);
				$arrKey = sprintf('(^|,)%d(,|$)', $subcategory['scatid']);
				$subcats['subcats'][] = array(
					'ecat',
					$ecatid,
					$this->getStrItems(&$prepSItm, array($arrKey))
				);
				$subcats['alias'][] = array(
					'scat',
					$subcategory['scatid'],
					$ecatid
				);
				$children = $this->buildSubcatTree($blogid, $catid, $ecatid, intval($subcategory['scatid']));
				if (!empty($children)) {
					foreach ($children['subcats'] as $child) {
						$subcats['subcats'][] = $child;
					}
					foreach ($children['alias'] as $child) {
						$subcats['alias'][] = $child;
					}
				}
			}
		}
		return $subcats;
	}

	// }}}

// -------------------------  install functions end  -------------------------

// ---------------------  Nucleus event functions start  ---------------------

	// {{{ function event_PreSendContentType($data)

	/**
	  * $ecatid to global var
	  * when pre send content type
	  *
	  * @access private
	  *
	  * @param array
	  *     'skin' object
	  *         (pass-by-reference) The SKIN-object that is handling the parse
	  *     'type' string
	  *         (pass-by-value)     Type of skinpart(one of 'index', 'item', 'archive',
	  *                             'archivelist', 'member', 'error', 'search',
	  *                             'imagepopup', 'fileparser')
	  **/
	function event_PreSendContentType($data)
	{
		global $CONF, $ecatid;
		if (empty($ecatid)) {
			if ($CONF['URLMode'] == 'pathinfo') {
				$pathdata = explode("/", strtolower(serverVar('REQUEST_URI')));
				if ($i = array_search($CONF['ECategoryKey'], $pathdata)) {
					if (!empty($pathdata[$i+1])) {
						$ecatid = intval($pathdata[$i+1]);
					}
				} elseif ($i = array_search('subcatid', $pathdata)) {
					if (!empty($pathdata[$i+1])) {
						$ecatid = $this->convertSubcatidToEcatid($pathdata[$i+1]);
					}
				} elseif ($i = array_search($CONF['CategoryKey'], $pathdata)) {
					if (!empty($pathdata[$i+1])) {
						$ecatid = $this->convertCatidToEcatid($pathdata[$i+1]);
					}
				}
			} else {
			$id=requestVar('catid');
				if ($id = intRequestVar($this->getRequestName())) {
					$ecatid = $id;
				} elseif ($id = intRequestVar('subcatid')) {
					$ecatid = $this->convertSubcatidToEcatid($id);
				} elseif ($id = intRequestVar('catid')) {
					$ecatid = $this->convertCatidToEcatid($id);
				}
			}
		}
		if (empty($ecatid)) {
			global $subcatid, $catid;
			if ($subcatid) {
				$ecatid = $this->convertSubcatidToEcatid($subcatid);
			} elseif ($catid) {
				$ecatid = $this->convertCatidToEcatid($catid);
			}
		}
	}

	// }}}
	// {{{ function event_PostAddCategory($data)

	/**
	  * $ecatid to global var
	  * when pre send content type
	  *
	  * @access private
	  *
	  * @param array
	  *     'blog' object
	  *         (pass-by-reference) reference to BLOG object
	  *     'name' string
	  *         (pass-by-value)     name of new category
	  *     'description' string
	  *         (pass-by-value)     description of new category
	  *     'catid' int
	  *         (pass-by-value)     New category ID
	  **/
	function event_PostAddCategory($data)
	{
		$ecatid = $this->createECategory($data['blog']->blogid, $data['name'], $data['description']);
		$column = array(
			'alias',
			'aliasid',
			'ecatid'
		);
		$values = array(
			'cat',
			$data['catid'],
			$ecatid
		);
		$this->dbh->insertInto(_NP_CATEGORYEX_ALIAS_TABLE, $column, $values);
	}

	// }}}
	// {{{ function event_PostDeleteCategory($data)

	/**
	  * $ecatid to global var
	  * when pre send content type
	  *
	  * @access private
	  *
	  * @param array
	  *     'catid' int
	  *         (pass-by-value)     New category ID
	  **/
	function event_PostDeleteCategory($data)
	{
		$ecatid  = $this->convertCatidToEcatid($data['catid']);
		$catTree = $this->getChildrenTree($ecatid, false, 0, false);
		$blogid  = $this->getBlogIDFromEcatID($ecatid);
		$aliasQ  = 'DELETE FROM '
				 . _NP_CATEGORYEX_ALIAS_TABLE
				 . ' WHERE `ecatid` = ?';
		$treeDQ  = 'DELETE FROM '
				 . _NP_CATEGORYEX_TREE_TABLE
				 . ' WHERE `ecatid` = ?';
		$itemSQ  = 'SELECT * FROM '
				 . _NP_CATEGORYEX_ITEM_TABLE
				 . " WHERE `idkey` = 'ecat' AND `keyid` = ?";
		$itemDQ  = 'DELETE FROM '
				 . _NP_CATEGORYEX_ITEM_TABLE
				 . " WHERE `idkey` = 'ecat' AND `keyid` = ?";
		$ecatSQ  = 'SELECT `ecats` FROM '
				 . _NP_CATEGORYEX_ECAT_TABLE
				 . ' WHERE `itemid` = ?';
		$ecatUQ  = 'UPDATE '
				 . _NP_CATEGORYEX_ECAT_TABLE
				 . 'SET `ecats` = ?'
				 . ' WHERE `itemid` = ?';
		$aliasP = $this->dbh->dbh->prepare($aliasQ);
		$treeDP = $this->dbh->dbh->prepare($treeDQ);
		$itemSP = $this->dbh->dbh->prepare($itemSQ);
		$itemDP = $this->dbh->dbh->prepare($itemDQ);
		$ecatSP = $this->dbh->dbh->prepare($ecatSQ);
		$ecatUP = $this->dbh->dbh->prepare($ecatUQ);
		foreach ($catTree as $cat) {
			$aliasP->execute(array($cat['ecatid']));
			$treeDP->execute(array($cat['ecatid']));
			$itemSP->execute(array($cat['ecatid']));
			$data  = $itemSP->fetch(PDO::FETCH_ASSOC);
			$items = explode(',', $data['itemids']);
			$itemDP->execute(array($cat['ecatid']));
			foreach ($items as $item) {
				$ecatSP->execute(array($item));
				$data  = $ecatSP->fetch(PDO::FETCH_ASSOC);
				$ecats = ltrim(preg_replace('|,?' . $cat['ecatid'] . '|', '', $data['ecats']), ',');
				$ecatUP->execute(array($ecats, $item));
			}
		}
	}

	// }}}





	// {{{ function createECategory($blogid, $catname, $catdesc, $parentid = 0, $ordid = 100)

	/**
	  * Create new enhanced category
	  *
	  * @access private
	  *
	  * @param int
	  *     Parent blog ID
	  * @param string
	  *     New category name
	  * @param string
	  *     New category description
	  * @param int
	  *     Parent enhanced category ID
	  * @param int
	  *     Sort order
	  *
	  * @return int
	  *     New enhanced category ID
	  **/
	function createECategory($blogid, $catname, $catdesc, $parentid = 0, $ordid = 100)
	{
		$table = _NP_CATEGORYEX_TREE_TABLE;
		$parts = array(
			'column' => 'alias',
			'where'  => array(
				'cname' => array('=', $catname),
			)
		);
		$aliasid = $this->dbh->resultOne($table, $parts);
		$columns = array(
			'blogid', 'parent', 'tleft', 'tright', 'cname', 'cdesc'
		);
		if ($parentid > 0) {
			$parts = array(
				'maximum' => 'tright',
				'where'   => array(
					'blogid' => array('=', $blogid),
					'ecatid' => array('=', $parentid)
				)
			);
			$maxRgt = $this->dbh->getMinMaxColumnIntArray($table, $parts);
			$right  = ($maxRgt['maximumtright']) ? $maxRgt['maximumtright'] : 1;
			$query  = 'UPDATE ' . $table
					. ' SET `tleft`=tleft+2'
					. ' WHERE `blogid` = ' . $blogid
					. ' AND `tleft` > ' . intval($right);
			$this->dbh->_exec($query);
			$query  = 'UPDATE ' . $table
					. ' SET `tright`=tright+2'
					. ' WHERE `blogid` = ' . $blogid
					. ' AND `tright` >= ' . intval($right);
			$this->dbh->_exec($query);
			$columns[] = 'corder';
			$values = array(
				$blogid, $parentid, $right, $right+1, $catname, $catdesc, $ordid
			);
		} else {
			$parts = array(
				'maximum' => 'tright',
				'where'   => array(
					'blogid' => array('=', $blogid),
				)
			);
			$maxRgt = $this->dbh->getMinMaxColumnIntArray($table, $parts);
			$right  = ($maxRgt['maximumtright']) ? $maxRgt['maximumtright'] : 0;
			$values = array(
				$blogid, 0, $right+1, $right+2, $catname, $catdesc
			);
		}
		$ecatid = $this->dbh->insertInto($table, $columns, $values);
		if (empty($alias)) {
			$alias = $ecatid;
		}
		$query = 'UPDATE ' . $table
			   . ' SET `alias`=' . intval($alias)
			   . ' WHERE `ecatid`=' . $ecatid;
		$this->dbh->_exec($query);
		return $ecatid;
	}

	// }}}
	// {{{ function reSortTree($treePart)

	/**
	  * Sort by order
	  *
	  * @access private
	  *
	  * @param array
	  *     Same parent categories
	  *
	  * @return bool
	  **/
	function reSortTree($treePart)
	{
		if (!is_array($treePart)) {
			return;
		}
		uasort($treePart, array(&$this, 'sortCatOrder'));
		foreach ($treePart as $value) {
			$this->treeData[] = $value;
			if (   !empty($this->treeParts[$value['ecatid']])
				&& is_array($this->treeParts[$value['ecatid']])  ) {
					$this->reSortTree($this->treeParts[$value['ecatid']]);
					unset($this->treeParts[$value['ecatid']]);
			}
		}
		return true;
	}

	// }}}
	// {{{ function sortCatOrder($aPart, $bPart)

	/**
	  * Comparison each categories
	  *
	  * @access private
	  *
	  * @param array
	  *     Category array
	  * @param array
	  *     Category array
	  *
	  * @return bool
	  **/
	function sortCatOrder($aPart, $bPart)
	{
		if ($aPart['corder'] < $bPart['corder']) {
			return false;
		} elseif ($aPart['corder'] == $bPart['corder'] &&  $aPart['ecatid'] < $bPart['ecatid']  ) {
			return false;
		}
		return true;
	}

	// }}}
	// {{{ function getBlogData($blogid)

	/**
	  * Build blog node
	  *
	  * @access private
	  *
	  * @param int
	  *     blog ID for build
	  *
	  * @return array
	  *     Blog data
	  **/
	function getBlogData($blogid)
	{
		global $manager;
		$b  =& $manager->getBlog(intval($blogid));
		$parts = array(
			'maximum' => 'tright',
			'where'   => array(
				'blogid' => array('=', $blogid),
			)
		);
		$maxRight = $this->dbh->getMinMaxColumnIntArray(_NP_CATEGORYEX_TREE_TABLE, $parts);
		$rootNode = array(
			'ecatid'     => 0,
			'blogid'    => $blogid,
			'parent'    => -1,
			'alias'  => $b->settings['bshortname'],
			'tleft'  => $maxRight['maximumtright'] - $maxRight['maximumtright'],
			'tright' => $maxRight['maximumtright'] + 1,
			'cname'  => $b->settings['bname'],
			'cdesc'  => $b->settings['bdesc'],
			'corder' => 0,
		);
		return $rootNode;
	}

	// }}}
	// {{{ function getTreeDataArray($res, $bData = false, $level = 0)

	/**
	  * Format category tree
	  *
	  * @access private
	  *
	  * @param array
	  *     categories
	  * @param array
	  *     blog data
	  * @param int
	  *     depth level
	  *
	  * @return array
	  *     Category tree
	  **/
	function getTreeDataArray($res, $bData = false, $level = 0)
	{
		global $manager;
		$treeKeys        = array();
		$this->treeParts = array();
		$this->treeData  = array();
		if (!empty($bData)) {
			$tree = array_merge(array($bData), $res);
		} else {
			$tree = $res;
		}
		$right = array();
		$count = 0;
		foreach ($tree as $row) {
			if (count($right) > 0) {
				while ($right[count($right) - 1] < $row['tright']) {
					array_pop($right);
				}
			}
			$depth = count($right);// + 1;
			if (!$level || $depth <= $level) {
				$pnode = $row['parent'];
//				$node  = $row['ecatid'];
//				$extra = array($this->getRequestName(), $node);
				$this->treeParts[$pnode][$count]          = $row;
				$this->treeParts[$pnode][$count]['level'] = $depth;
//				$this->treeParts[$pnode][$count]['link']  = createBlogidLink($row['blogid'],$extra);
				$right[]                                  = $row['tright'];
				$treeKeys[]                               = $pnode;
			}
			$count++;
		}
		$treeKeys = array_unique($treeKeys);
		foreach ($treeKeys as $key) {
			$this->reSortTree($this->treeParts[$key]);
		}

// <Temporary measure>
		foreach ($this->treeData as $key => $value) {
			$catid = false;
			if (intval($value['ecatid']) > 0) {
				$extra = array();
				$exTmp = $this->convertEcatidToCatid($value['ecatid']);
				if ($exTmp['alias'] == 'scat') {
					$extra['subcatid'] = $exTmp['aliasid'];
					$rootCat  = $this->getRootCategory($value['ecatid']);
					$tmpCatid = $this->convertEcatidToCatid($rootCat['ecatid']);
					$catid    = $tmpCatid['aliasid'];
				} else {
					$catid = $exTmp['aliasid'];
				}
				$this->treeData[$key]['uri'] = createCategoryLink($catid, $extra);
			} elseif (intval($value['ecatid']) === 0) {
				$this->treeData[$key]['uri'] = createBlogidLink($value['blogid']);
			}
		}
// </Temporary measure>

		return $this->treeData;
	}

	// }}}
	// {{{ function getParentsArray($id)

	/**
	  * Get parents categories array
	  *
	  * @access private
	  *
	  * @param int
	  *     enhanced category ID
	  *
	  * @return array
	  *     non format categories array
	  **/
	function getParentsArray($id)
	{
		$parts = array(
			'column' => '*',
			'where'  => array(
				'ecatid' => array('=', $id),
			)
		);
		$ecat  = $this->dbh->getSingleRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
		$parts = array(
			'column' => '*',
			'where'  => array(
				'tleft'  => array('<=', $ecat['tleft']),
				'tright' => array('>=', $ecat['tright']),
				'blogid' => array('=',  $ecat['blogid']),
			),
			'order'  => array(
				'tleft' => 'ASC'
			)
		);
		return $this->dbh->getAllRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
	}
	// }}}

// -------------------------  pubric functions start  -------------------------


// <Temporary measure>
/**
  * It's the function for measure in a transition period,
  * so these will be eliminated in the future.
  **/
	// {{{ function convertEcatidToCatid($ecatid)

	/**
	  * Convert from $ecatid to $catid
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return array
	  *     alias name
	  *     alias ID
	  **/
	function convertEcatidToCatid($ecatid)
	{
		$parts = array(
			'column' => array(
				'aliasid',
				'alias',
			),
			'where'  => array(
				'ecatid' => array('=', $ecatid),
			)
		);
		return $this->dbh->getSingleRowAssoc(_NP_CATEGORYEX_ALIAS_TABLE, $parts);
	}

	// }}}
	// {{{ function convertCatidToEcatid($cid)

	/**
	  * Convert from $catid to $ecatid
	  *
	  * @access public
	  *
	  * @param int 
	  *     Nucleus default category ID
	  *
	  * @return int
	  *     Enhanced category ID
	  **/
	function convertCatidToEcatid($cid)
	{
		$parts = array(
			'column' => 'ecatid',
			'where'  => array(
				'aliasid' => array('=', $cid),
				'alias'   => array('=', 'cat'),
			)
		);
		return intval($this->dbh->resultOne(_NP_CATEGORYEX_ALIAS_TABLE, $parts));
	}

	// }}}
	// {{{ function convertSubcatidToEcatid($scid)

	/**
	  * Convert from $catid to $ecatid
	  *
	  * @access public
	  *
	  * @param int 
	  *     NP_MultipleCategories default Subcategory ID
	  *
	  * @return int
	  *     Enhanced category ID
	  **/
	function convertSubcatidToEcatid($scid)
	{
		$parts = array(
			'column' => 'ecatid',
			'where'  => array(
				'aliasid' => array('=', $scid),
				'alias'   => array('=', 'scat'),
			)
		);
		return intval($this->dbh->resultOne(_NP_CATEGORYEX_ALIAS_TABLE, $parts));
	}
	// }}}
// </Temporary measure>


/**
  * The function which was set up to refer to it from other plugins
  **/

	// {{{ function getRequestName()

	/**
	  * URL query name
	  *
	  * @access public
	  *
	  * @return string
	  *     URL query name
	  **/
	function getRequestName()
	{
		return 'ecatid';
	}

	// }}}
	// {{{ function getBlogIDFromEcatID($id)

	/**
	  * Get ID for the blog including category
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return int
	  *     blog ID
	  **/
	function getBlogIDFromEcatID($id)
	{
		$parts = array(
			'column' => 'blogid',
			'where'  => array(
				'ecatid' => array('=', $id),
			)
		);
		$ibd = $this->dbh->resultOne(_NP_CATEGORYEX_TREE_TABLE, $parts);
		return intval($bid);
	}

	// }}}
	// {{{ function getRootCategory($id)

	/**
	  * Get array for root category
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return array
	  *     Root category
	  **/
	function getRootCategory($id)
	{
		$data = $this->getParentsArray($id);
		return $data[0];
	}

	// }}}
	// {{{ function getParentsTree($id, $bFlag = false, $level = 0)

	/**
	  * Get ancestor categories
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  * @param bool 
	  *     "LEVEL" but when being big sufficiently, the tree which made blog a root is returned.
	  * @param int 
	  *     The depth of the hierarchy I try to acquire
	  *
	  * @return array
	  *     Category tree
	  **/
	function getParentsTree($id, $bFlag = false, $level = 0)
	{
		$data  = $this->getParentsArray($id);
		$depth = count($tmpTree);
		if ($level && ($level <= $depth)) {
			$trush = array_splice($data, 0, $depth - $level);
		} elseif ((!$level || $level > $depth) && $bFlag) {
			$bData = $this->getBlogData($data[0]['blogid']);
		}
		return $this->getTreeDataArray($data, $bData);
	}

	// }}}
	// {{{ function getChildrenTree($id, $bflag = true, $level = 0, $flg = ture)

	/**
	  * Get offsprings categories
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID or blog ID
	  * @param bool 
	  *     The tree which made blog a root is returned.
	  * @param int 
	  *     The depth of the hierarchy I try to acquire
	  * @param bool 
	  *     Format data is returned.
	  *
	  * @return array
	  *     Category tree
	  **/
	function getChildrenTree($id, $bflag = true, $level = 0, $flg = ture)
	{
		$bData = false;
		if ($bflag) {
			$bData = $this->getBlogData($id);
			$ecat  = $bData;
		} else {
			$parts = array(
				'column' => '*',
				'where'  => array(
					'ecatid' => array('=', $id),
				)
			);
			$ecat = $this->dbh->getSingleRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
		}
		$parts = array(
			'column' => '*',
			'where'  => array(
				'tleft'  => array('>=', $ecat['tleft']),
				'tright' => array('<=', $ecat['tright']),
				'blogid' => array('=',  $ecat['blogid']),
			),
			'order'  => array(
				'tleft' => 'ASC'
			)
		);
		$res = $this->dbh->getAllRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
		if ($flg) {
			return $this->getTreeDataArray($res, $bData, $level);
		} else {
			return $res;
		}
	}

	// }}}
	// {{{ function isValidECategory($ecatid)

	/**
	  * Confirmation of whether a category exists really
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return bool
	  **/
	function isValidECategory($ecatid)
	{
		$parts = array(
			'column' => '*',
			'where'  => array(
				'ecatid' => array('=',  $ecatid),
			),
		);
		return $this->dbh->resultOne(_NP_CATEGORYEX_TREE_TABLE, $parts);
	}

	// }}}
	// {{{ function getECatNameFromID($ecatid)

	/**
	  * The name is acquired from the ID.
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return string
	  *     Category name
	  **/
	function getECatNameFromID($ecatid)
	{
		if ($this->isValidECategory($ecatid)) {
			$parts = array(
				'column' => array(
					'cname' => 'ecatname'
				),
				'where'  => array(
					'ecatid' => array('=',  $ecatid),
				),
			);
			return $this->dbh->resultOne(_NP_CATEGORYEX_TREE_TABLE, $parts);
		}
	}

	// }}}
	// {{{ function getECatDescFromID($ecatid)

	/**
	  * The description is acquired from the ID.
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return string
	  *     Category description
	  **/
	function getECatDescFromID($ecatid)
	{
		if ($this->isValidECategory($ecatid)) {
			$parts = array(
				'column' => array(
					'cdesc' => 'ecatdesc'
				),
				'where'  => array(
					'ecatid' => array('=',  $ecatid),
				),
			);
			return $this->dbh->resultOne(_NP_CATEGORYEX_TREE_TABLE, $parts);
		}
	}

	// }}}
	// {{{ function getECatDataFromID($ecatid)

	/**
	  * The category data is acquired from the ID.
	  *
	  * @access public
	  *
	  * @param int 
	  *     Enhanced category ID
	  *
	  * @return array
	  *     Category data
	  **/
	function getECatDataFromID($ecatid)
	{
		if ($this->isValidECategory($ecatid)) {
			$parts = array(
				'column' => '*',
				'where'  => array(
					'ecatid' => array('=',  $ecatid),
				),
			);
			return $this->dbh->getSingleRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
		}
	}

	// }}}
	// {{{ function getECatIDsFromName($ecatname)

	/**
	  * The category IDs are acquired from the name.
	  *
	  * @access public
	  *
	  * @param string 
	  *     Enhanced category name
	  *
	  * @return array
	  *     Category IDs
	  **/
	function getECatIDsFromName($ecatname)
	{
		$parts = array(
			'column' => 'ecatid',
			'where'  => array(
				'cname' => array('=',  $ecatname),
			),
		);
		$data = $this->dbh->getAllRowAssoc(_NP_CATEGORYEX_TREE_TABLE, $parts);
		if ($data) {
			foreach ($data as $value) {
				$retData[] =$value['ecatid'];
			}
			return $retData;
		}
		return false;
	}

	// }}}
	// {{{ function getItemsIDFromECatID($ecatid, $arrayFlg = true)

	/**
	  * The ID for the item included in a category is acquired.
	  *
	  * @access public
	  *
	  * @param int 
	  *     The ID for the enhanced category which tries to do the income
	  * @param bool 
	  *     true  => Arrangement is returned.
	  *     false => String is returned.
	  *
	  * @return array or string
	  *     Item IDs
	  **/
	function getItemsIDFromECatID($ecatid, $arrayFlg = true)
	{
		if ($this->isValidECategory($ecatid)) {
			$parts = array(
				'column' => 'itemids',
				'where'  => array(
					'idkey' => array('=',  'ecat'),
					'keyid' => array('=',  $ecatid),
				),
			);
			$data = $this->dbh->resultOne(_NP_CATEGORYEX_ITEM_TABLE, $parts);
			if ($data) {
				if ($arrayFlg) {
					return explode(',', $data);
				} else {
					return $data;
				}
			}
		}
		return false;
	}

	// }}}
	// {{{ function getECategoriesIDFromItemID($itemid, $arrayFlg = true)

	/**
	  * The ID for the category by which an item is included is acquired.
	  *
	  * @access public
	  *
	  * @param int 
	  *     The ID for the item which tries to do the income
	  * @param bool 
	  *     true  => Arrangement is returned.
	  *     false => String is returned.
	  *
	  * @return array or string
	  *     Category IDs
	  **/
	function getECategoriesIDFromItemID($itemid, $arrayFlg = true)
	{
		if ($this->isValidItem($itemid)) {
			$parts = array(
				'column' => 'ecats',
				'where'  => array(
					'itemid' => array('=',  $itemid),
				),
			);
			$data = $this->dbh->resultOne(_NP_CATEGORYEX_ECAT_TABLE, $parts);
			if ($data) {
				if ($arrayFlg) {
					return explode(',', $data);
				} else {
					return $data;
				}
			}
		}
		return false;
	}

	// }}}











}
